package it.uniroma2.structure;

import it.uniroma2.gestoreontologie.Gestore;
import it.uniroma2.model.wizard.Parametri;
import it.uniroma2.orchestrator.Orchestrator;

import java.io.File;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.predic8.wsdl.Message;


public class Service extends Node implements Serializable {
	private String namespace;
	private PartnerLink partnerLink;
	private Vector<Parameter> parameterInVec;
	private Vector<Parameter> parameterOutVec;
	private Vector<LinkServiceNode> inLinksService;
	private Vector<LinkServiceNode> outLinksService;
	private Wsdl_Service wsdlReaderService;
	
	private String compatNS;
	
	private String nome;
	private String input;
	private String output;
	private String descrizione;
	private String wsdl;
	private String url;
	private int rank;
	private boolean model;
	private String level;
	private String type;
	private String superclass;
	
	
	private Gestore gestore;
	private String mainOperation;
	private String parameterOperation;
	private Hashtable internalParameter;
	private Hashtable externalParameter;
	private Vector<String> preCondizione;
	private Vector<String> postCondizione;
	private Vector<String> preCondizioneNoLoop;
	private Vector<String> postCondizioneNoLoop;
	private Vector<String> preCondizioneNoNLP;
	private Vector<String> postCondizioneNoNLP;
	
	
	
	public Service(){
		parameterInVec = new Vector<Parameter>();	
		parameterOutVec = new Vector<Parameter>();
		this.inLinksService = new Vector<LinkServiceNode>();
		this.outLinksService = new Vector<LinkServiceNode>();
	}
	
	
	public Service(String name){
		this();
		this.name = name;
		
		this.gestore = new Gestore(Parametri.NS, Orchestrator.servOnt);
		this.rank = 0;
		this.input = new String();
		this.output = new String();
		this.nome = name;
		this.model = false;
		this.level = new String();
		this.type = new String();
		this.superclass = new String();
		this.fillService();
		
		
		this.wsdlReaderService = new Wsdl_Service(wsdl);
		
		this.namespace = this.wsdlReaderService.getTargetNamespace();
		//this.setParameterIn();
		//this.setParameterOut();
	}
	
	public void fillService(){
		this.findMainOperation();
		this.findParameterOperation();
		this.findURL();
		this.findWSDL();
		this.findInternalParameter();
		this.findExternalParameter();
		this.findFromServiceParameter();
		this.findToServiceParameter();
		this.findPreCond();
		this.findPostCond();
		this.findSuperclass();
		
		
	}
	
	private void findMainOperation(){
		this.mainOperation = new String();
		mainOperation = gestore.recuperaDataPro(nome, "Main_Operation");
	}
	
	private void findParameterOperation(){
		this.parameterOperation = new String();
		parameterOperation = gestore.recuperaDataPro(nome, "Parameter_Operation");
	}
	
	private void findURL(){
		this.url = new String();
		url = gestore.recuperaDataPro(nome, "URL");
	}
	
	private void findWSDL(){
		this.wsdl = new String();
		wsdl = gestore.recuperaDataPro(nome, "WSDL");
	}
	
	private void findInternalParameter(){
		this.internalParameter = new Hashtable();
		Vector<String> propVect = gestore.recuperaProprietaVect(nome, "has_internal_input");
		for (int k = 0; k < propVect.size(); k++){
			String tmp = propVect.elementAt(k);
			String tmp2 = gestore.recuperaProprieta(tmp, "has_type");
			internalParameter.put(tmp, tmp2);
			Parameter par = new Parameter(tmp , tmp2);
			par.setParameterType("internal");
			this.parameterInVec.add(par);
		}
	}
	
	private void findExternalParameter(){
		this.externalParameter = new Hashtable();
		Vector<String> propVect = gestore.recuperaProprietaVect(nome, "has_external_input");
		for (int k = 0; k < propVect.size(); k++){
			String tmp = propVect.elementAt(k);
			String tmp2 = gestore.recuperaProprieta(tmp, "has_type");
			externalParameter.put(tmp, tmp2);
			Parameter par = new Parameter(tmp , tmp2);
			par.setParameterType("external");
			this.parameterInVec.add(par);
		}
	}
	
	private void findFromServiceParameter(){
		Vector<String> propVect = gestore.recuperaProprietaVect(nome, "has_fromservice_input");
		for (int k = 0; k < propVect.size(); k++){
			String tmp = propVect.elementAt(k);
			String tmp2 = gestore.recuperaProprieta(tmp, "has_type");
			Parameter par = new Parameter(tmp , tmp2);
			par.setParameterType("fromservice");
			this.parameterInVec.add(par);
		}
	}
	
	private void findToServiceParameter(){
		Vector<String> propVect = gestore.recuperaProprietaVect(nome, "has_toservice_output");
		for (int k = 0; k < propVect.size(); k++){
			String tmp = propVect.elementAt(k);
			String tmp2 = gestore.recuperaProprieta(tmp, "has_type");
			Parameter par = new Parameter(tmp , tmp2);
			par.setParameterType("toservice");
			this.parameterOutVec.add(par);
		}
	}
	
	private void findPreCond(){
		this.preCondizione = new Vector();
		this.preCondizioneNoLoop = new Vector();
		preCondizione = gestore.recuperaProprietaVect(nome, "pre_condition");
		preCondizioneNoLoop = gestore.recuperaProprietaVect(nome, "pre_condition");
		preCondizioneNoNLP = gestore.recuperaProprietaVect(nome, "pre_condition");
		Vector<String> tmp = gestore.cercaIndividual("Classificator");
		for (int i = 0; i < tmp.size(); i++){
			tmp.set(i, gestore.eliminaNs(tmp.elementAt(i)));
		}
		for (int i = 0; i < tmp.size(); i++){
			for (int j = 0; j < preCondizioneNoLoop.size(); j++){
				if (tmp.elementAt(i).equals(preCondizioneNoLoop.elementAt(j))){
					this.preCondizioneNoLoop.remove(j);
					break;
				}
			}
		}
		tmp = gestore.cercaIndividual("Feature_model");
		for (int i = 0; i < tmp.size(); i++){
			tmp.set(i, gestore.eliminaNs(tmp.elementAt(i)));
		}
		for (int i = 0; i < preCondizioneNoNLP.size(); i++){
			boolean remove = true;
			for (int j = 0; j < tmp.size(); j++){
				if (tmp.elementAt(j).equals(preCondizioneNoNLP.elementAt(i))){
					remove = false;
				}
			}
			if(remove){
				preCondizioneNoNLP.remove(i);
				i--;
			}
		}
		
		
	}
	private void findPostCond(){
		this.postCondizione = new Vector();
		this.postCondizioneNoLoop = new Vector();
		postCondizione = gestore.recuperaProprietaVect(nome, "post_condition");
		postCondizioneNoLoop = gestore.recuperaProprietaVect(nome, "post_condition");
		Vector<String> tmp = gestore.cercaIndividual("Classificator");
		for (int i = 0; i < tmp.size(); i++){
			tmp.set(i, gestore.eliminaNs(tmp.elementAt(i)));
		}
		for (int i = 0; i < tmp.size(); i++){
			for (int j = 0; j < postCondizioneNoLoop.size(); j++){
				if (tmp.elementAt(i).equals(postCondizioneNoLoop.elementAt(j))){
					this.postCondizioneNoLoop.remove(j);
					break;
				}
			}
		}
	}
	
	private void findSuperclass(){
		String tmp2 = new String();
		Vector<String> ind = gestore.cercaIndividual("Classificator");
		for(String tmp : ind){
			tmp2 = gestore.eliminaNs(tmp);
			if(tmp2.equals(nome)){
				this.superclass = "Classificator";
			}
		}
		ind = gestore.cercaIndividual("Feature_model");
		for(String tmp : ind){
			tmp2 = gestore.eliminaNs(tmp);
			if(tmp2.equals(nome)){
				this.superclass = "Feature_model";
			}
		}
		ind = gestore.cercaIndividual("NLP_Processing");
		for(String tmp : ind){
			tmp2 = gestore.eliminaNs(tmp);
			if(tmp2.equals(nome)){
				this.superclass = "NLP_Processing";
			}
		}
	}
	
	public String getMainOperation(){
		return this.mainOperation;
	}
	
	public String getParameterOperation(){
		return this.parameterOperation;
		
	}
	public String getInput() {
		return input;
	}

	public void setInput(String input) {
		this.input = input;
	}

	public String getOutput() {
		return output;
	}

	public void setOutput(String output) {
		this.output = output;
	}
	public String getNome(){
		return nome;
	}

	public Vector<String> getPreCondizione() {
		return preCondizione;
	}
	
	public Vector<String> getPreCondizioneNoLoop() {
		return preCondizioneNoLoop;
	}
	
	public Vector<String> getPrecondizioneNoNLP(){
		return preCondizioneNoNLP;
	}

	public void setPreCondizione(String servizio) {
		this.preCondizione.addElement(servizio);
	}

	public Vector<String> getPostCondizione() {
		return postCondizione;
	}
	
	public Vector<String> getPostCondizioneNoLoop() {
		return postCondizioneNoLoop;
	}

	public void setPostCondizione(String servizio) {
		this.postCondizione.addElement(servizio);
	}
	
	public String getSuperclass(){
		return this.superclass;
	}
	
	public void setRank(int rank){
		this.rank = rank;
	}
	
	public boolean hasPreCondizione(){
		if(this.preCondizione.isEmpty()) return false;
		return true;
	}
	
	public boolean hasPostCondizione(){
		if(this.postCondizione.isEmpty()) return false;
		return true;
	}
	//This method calculates ranks of classifier
	public void rankCalc(String level, String type){
		this.level = level;
		this.type = type;
		if(type.compareToIgnoreCase(Parametri.FEATURE) == 0 || type.compareToIgnoreCase(Parametri.TARGET) == 0){
			if(level.compareToIgnoreCase(Parametri.LVLDOC) == 0){
				this.rank = 1;
			} else if(level.compareToIgnoreCase(Parametri.LVLSENT) == 0){
				this.rank = 2;
			} else if(level.compareToIgnoreCase(Parametri.LVLFEAT) == 0){
				this.rank = 3;
			}
		}else{
			if(level.compareToIgnoreCase(Parametri.LVLDOC) == 0){
				this.rank = 4;
			} else if(level.compareToIgnoreCase(Parametri.LVLSENT) == 0){
				this.rank = 5;
			} else if(level.compareToIgnoreCase(Parametri.LVLFEAT) == 0){
				this.rank = 6;
			}
		}
		
	}
	
	public void createAnnotationFile(){
		
		
		try {
			DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			
			Document doc = docBuilder.newDocument();
			Element rootElement = doc.createElement("annotation");
			doc.appendChild(rootElement);
			Element tagLevel = doc.createElement("tagLevel");
			rootElement.appendChild(tagLevel);
			
			if(level.compareToIgnoreCase(Parametri.LVLDOC) == 0){
				tagLevel.appendChild(doc.createTextNode("Document"));
			} else if(level.compareToIgnoreCase(Parametri.LVLSENT) == 0){
				tagLevel.appendChild(doc.createTextNode("Sentence"));
			} else if(level.compareToIgnoreCase(Parametri.LVLFEAT) == 0){
				tagLevel.appendChild(doc.createTextNode("Featuring"));
			}
			if(type.compareToIgnoreCase(Parametri.TERNARY)== 0){
				Element polari = doc.createElement("polarities");
				rootElement.appendChild(polari);
				Element pola = doc.createElement("Polarity");
				pola.appendChild(doc.createTextNode("Positive"));
				polari.appendChild(pola);
				Element pola2 = doc.createElement("Polarity");
				pola2.appendChild(doc.createTextNode("Negative"));
				polari.appendChild(pola2);
				Element pola3 = doc.createElement("Polarity");
				pola3.appendChild(doc.createTextNode("Neutral"));
				polari.appendChild(pola3);
				
				TransformerFactory transformerFactory = TransformerFactory.newInstance();
				Transformer transformer = transformerFactory.newTransformer();
				DOMSource source = new DOMSource(doc);
				StreamResult result = new StreamResult(new File("annotation.xml"));
		 
				//StreamResult result = new StreamResult(System.out);
		 
				transformer.transform(source, result);
			}
			
			
		} catch (ParserConfigurationException e) {
			System.out.println("Error creating xml file");
			e.printStackTrace();
		} catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
	}
	
	public int getRank(){
		return this.rank;
	}

	public String getUrl(){
		return this.url;
	}
	public String getWsdl(){
		return this.wsdl;
	}
	public void setUrl(String url){
		this.url = url;
	}
	public void setWsdl(String wsdl){
		this.wsdl = wsdl;
	}
	public boolean getModel(){
		return model;
	}
	public void setModel(boolean model){
		this.model = model;
	}
	private void setParameterIn(){
		Vector<String> vecIn = this.wsdlReaderService.getElementIn(this.mainOperation);
		for(String temp : vecIn){
			Parameter par = new Parameter(temp);
			this.parameterInVec.addElement(par);
		}
	}
	
	
	private void setParameterOut(){
		Vector<String> vecOut = this.wsdlReaderService.getElementOut(this.mainOperation);
		for(String temp : vecOut){
			Parameter par = new Parameter(temp);
			this.parameterOutVec.addElement(par);
		}
	}
	
	
	public void setWsdlReader(String pathWsdl){
		this.wsdlReaderService = new Wsdl_Service(pathWsdl);
	}
	
	
	public void setOperation(String nameOp){
		this.mainOperation = nameOp;
	}
	
	
	public void getResourceFromWsdl(){
//		this.message = this.wsdlReaderService.getMessageInput(this.mainOperation);
		this.namespace = this.wsdlReaderService.getTargetNamespace();
	}
	
	
	public void addInLink(LinkServiceNode s){
		this.inLinksService.addElement(s);
	}
	
	
	public void addOutLink(LinkServiceNode s){
		this.outLinksService.addElement(s);
	}
	
	
	public void setAssign(Parameter from, Parameter to, LinkServiceNode l){
		l.addCopy(from, to);
	}
	
	
	public Vector<Parameter> getParameterIn(){
		return this.parameterInVec;
	}
	
	
	public Vector<Parameter> getParameterOut(){
		return this.parameterOutVec;
	}
	
	public void setParameterIn(Vector<Parameter> parameterInVec){
		this.parameterInVec = parameterInVec;
	}
	
	
	public void setParameterOut(Vector<Parameter> parameterOutVec){
		this.parameterOutVec = parameterOutVec;
	}
	
	public void addParameterIn(Parameter parameterIn){
		this.parameterInVec.add(parameterIn);
	}
	
	
	public void addParameterOut(Parameter parameterOut){
		this.parameterOutVec.add(parameterOut);
	}
	
	
	@Override
	public String toString() {
		return this.getName();
	}
	
	
	public String getDescription() {
		String output = "name=\"" + this.name + "\" partnerLink=\"" + this.partnerLink.getName() + "\" operation=\"" + this.mainOperation + 
				"\" portType=\"" + this.compatNS + ":" + this.wsdlReaderService.getPortType(this.mainOperation) + "\" ";
		
		for(Parameter temp : this.parameterInVec){
			if(temp.getVariable()==null){
				output = output + "inputVariable=\"\" ";
			}else{
				output = output + "inputVariable=\"" + temp.getVariable().getName() + "\" ";
			}
			
			break;
		}
		for(Parameter temp : this.parameterOutVec){
			if(temp.getVariable()==null){
				output = output + "outputVariable=\"\" ";

			}else{
				output = output + "outputVariable=\"" + temp.getVariable().getName() + "\" ";
			}
			
			break;
		}
		return output;
	}
	
	
	@Override
	public String toBPEL(){
		String output = "<" + invokeTag + " " + this.getDescription() + "/>\n";
		for(LinkServiceNode tempLink : this.outLinksService){
			output = output + tempLink.toBPEL();
		}
		
		return output;
	}
	
	
	public String getNamespace() {
		return this.wsdlReaderService.getTargetNamespace();
	}
	
	
	public void setNamespace(String namespace) {
		this.namespace = namespace;
	}
	
	
	public PartnerLink getPartnerLink() {
		return partnerLink;
	}
	
	
	public void setPartnerLink(PartnerLink partnerLink) {
		this.partnerLink = partnerLink;
	}
	
	
	@Override
	public boolean equals(Object s) {
		if(s instanceof Service){
			return (((Service) s).getName().equals(this.name));
		}
		return false;
	}


	public void removeEveryLink() {
		Node temp;
		for(LinkServiceNode tempInLink : this.inLinksService){
			temp = tempInLink.getFromNode();
			if(temp instanceof Service){
				((Service)temp).removeOutLink(tempInLink);
			} else if (temp instanceof Receive) {
				((Receive)temp).removeOutLink(tempInLink);
			}
			
		}
		
		for(LinkServiceNode tempOutLink : this.outLinksService){
			temp = tempOutLink.getFromNode();
			if(temp instanceof Service){
				((Service)temp).removeInLink(tempOutLink);
			} else if (temp instanceof Reply) {
				((Reply)temp).removeInLink(tempOutLink);
			}
		}
	}


	private void removeOutLink(LinkServiceNode outLink) {
		this.outLinksService.remove(outLink);
		
	}


	private void removeInLink(LinkServiceNode inLink) {
		this.inLinksService.remove(inLink);
		
	}


	public void setCompatNS(String ns) {
		this.compatNS = ns;
	}
	
	
	public String getCompatNS() {
		return this.compatNS;
	}


	public String getInputMessage() {
		Message msg = this.wsdlReaderService.getMessageInput(mainOperation);
		return msg.getName();
	}


	public String getOutputMessage() {
		Message msg = this.wsdlReaderService.getMessageOutput(mainOperation);
		return msg.getName();
	}
	
	
	public String getPartIn(){
		Message msg = this.wsdlReaderService.getMessageInput(mainOperation);
		return msg.getParts().get(0).getName();
	}
	
	
	public String getPartOut(){
		Message msg = this.wsdlReaderService.getMessageOutput(mainOperation);
		return msg.getParts().get(0).getName();
	}


	public String getPort() {
		return this.wsdlReaderService.getPortType(this.mainOperation);
	}


	public String getServiceFromWSDL() {
		return this.wsdlReaderService.getService();
	}
	
	
	public String getHierarchyElement(String message, String prefix	){
		String output = this.wsdlReaderService.getElements(message, prefix);
		
		return output;
	}


	public Vector<LinkServiceNode> getOutLinks() {
		return this.outLinksService;
	}
}
